The TradDriverLoaderLib provides a bunch of routines helpful for installing traditional Mac OS device drivers (DRVRs). While there have been many samples of how to do this in the past, this sample has a number of advantages:
1. It works. Some of the other samples out there don’t, at least not in all possible cases.
2. It mimicks the API of the PCI “DriverLoaderLib” as much as possible, which makes the TradDriverLoaderLib easier to use in an environment supporting both PCI native drivers (ndrv) and traditional Mac OS drivers (DRVR).
The PCI “DriverLoaderLib” is documented in “Designing PCI Cards and Drivers for Power Macintosh Computers"; you can FTP an electronic copy from:
4. I’m in the process of revising the DTS sample DRVRs to use this library where appropriate.
5. You can build it into both 68K and PPC clients.
Using the Sample
Sample Contents
The distribution contains the following files:
• Read Me — TradDriverLoaderLib — This file.
• TradDriverLoaderLib.c — C source for the library.
• TradDriverLoaderLib.h — C interface to the library.
• TradDriverLoaderLib.p — Pascal interface to the library.
• MissingLibraryRoutines.c — C glue that implements certain system calls that should be included in InterfaceLib but are not. You must add this glue to any PPC client of DriverLoaderLib. 68K clients should not include this glue.
• TestTradDriverLoader.p — Pascal source to the test application.
• TestTradDriverLoader-68K.µ — A project that builds a 68K application to test the library.
• TestTradDriverLoader-68K — The compiled binary of the above.
• TestTradDriverLoader-PPC.µ — A project that builds a PPC application to test the library.
• TestTradDriverLoader-PPC — The compiled binary of the above.
• TestDriver — A folder containing a test DRVR that’s used by the test application.
What You Need
This sample is built in CodeWarrior 11 using both the Pascal and C compilers.
How to Build
This section gives you instructions on how to build the TestTradDriverLoaderLib test program. Actually using the library from your program is covered in the next two sections.
To build the test program:
1. Open the “Metrowerks Build Script” AppleScript.
2. Run it.
3. Choose the “TradDriverLoaderLib” folder
4. Run the “TestTradDriverLoader-68K” or “TestTradDriverLoader-PPC” application.
This will compile and execute the test program which, amongst other things, installs and removes 494 copies of the test driver!
Using the API from C
Installing and opening a DRVR using TradDriverLoaderLib is very simple. You need to take the following steps:
1. Add “TradDriverLoaderLib.c” to your project.
2. If you are building a PPC client, add “MissingLibraryRoutines.c” to your project.
3. Include “TradDriverLoaderLib.h” in your source.
4. When you want to install a driver, execute the following:
All of the routines in the API are described in detail in the comments in the “TradDriverLoaderLib.h” file. I suggest you look there for more in-depth information about the services provided by the library.
Internal Implementation Details
The most important routine in TradDriverLoaderLib is TradInstallDriverFromPtr. Both TradInstallDriverFromHandle and TradInstallDriverFromResource call through to this routine. This routine takes a pointer to an DRVR that has been loaded in the system heap and creates a Device Control Entry (DCE) in the system’s unit table for that driver. The driver uses an interesting variant of DriverInstall (namely DriverInstallReserveMem) to ensure that the driver’s DCE is loaded as low in the system heap as possible.
When you call TradInstallDriverFromHandle, it creates an appropriately sized pointer block in the system heap and copies the DRVR you supply into that block. It then calls TradInstallDriverFromPtr on that block. This ensures that the driver code is loaded as low in the system heap as possible.
TradInstallDriverFromResource simply calls through to TradInstallDriverFromHandle.
The source code contains many comments on the specific details of installing a driver in the unit table.
Sharing Driver Code Between Multiple Instances
Because TradInstallDriverFromPtr takes a pointer to the driver and just jams into the dCtlDriver field of the DCE without interpretation, you can use this routine to share code between device drivers. This is useful for things like serial drivers, which traditionally install two different drivers with different names. You can create one big chunk of code that has multiple driver headers in it, and then install that code into two different DCE’s using TradInstallDriverFromPtr.
Obviously, this is not for the faint of heart (–:
dRAMBased and dNeedLock
The dctlFlags field of the DCE has a bit known as dRAMBased. Most people interpret this bit as:
1 ==> device driver is in RAM
0 ==> driver is in ROM
This interpretation is wrong! The name dRAMBased is a historical artifact of the time when the system had ROM based drivers for hardware and RAM based driver for desk accessories.
The correct interpretation of this bit is:
1 ==> the dCtlDriver field of the DCE is a handle
0 ==> the dCtlDriver field of the DCE is a pointer
So you don’t have to set this bit to load your driver into RAM. In additon, pointer-based driver are also more efficient because the Device Manager does not have to dereference the handle each time.
dNeedLock controls whether the Device Manager locks the DCE for your driver. If you set dNeedLock, the Device Manager will lock your driver’s DCE whenever your driver is active.
Devices that operate at interrupt time (either accepting asynchronous requests or completing requests) should never have dRAMBased set and should always have dNeedLock set. Failing to do this will result in the Device Manager calling Memory Manager routines at interrupt time, with unpredictable and possibly catastrophic results.
TradDriverLoaderLib implements this recomendation and always loads drivers into RAM, making sure that dRAMBased is clear and dNeedLock is set.
Setting dNeedLock
This sample always sets dNeedLock. This is necessary to prevent the Device Manager from locking and unlocking the DCE while the driver operates. Instead the Device Manager notices that dNeedLock is set and locks the DCE once when the driver is opened. This is good for much the same reasons :
1. It saves a few CPU cycles.
2. It avoids the Device Manager calling the Memory Manager HLock and HUnlock routines at interrupt time. Devices that operate at interrupt time (either accepting asynchronous requests or completing requests) should always have dNeedLock set.
Caveats
There are no known caveats at this time.
Credits and Version History
Thanks for François Grieu for his invaluable contribution to this effort.
If you find any problems with this stuff, mail <DevSupport@apple.com> with “Attn: Quinn” as the first line of your mail and I’ll try to fix them up.
1.0b1 was distributed to a couple of developers.
1.0b2 is the first official release version.
1.0b3 incorporates changes from the original DTS reviewers. In addition, TradDriverLoaderLib now calls CloseDriver instead of FSClose.
1.0b4 incorporates changes and suggestions from François Grieu. Tidied up the description of dNeedLock and dRAMBased in the documentation, and made TradGetDriverInformation more paranoid about bogus drivers.
1.0b5 can now build PPC native TradDriverLoaderLib using snazzy MixedMode glue for DriverInstallReserveMem (which is not in InterfaceLib). Also increased kMaximumNumberOfUnitTableEntries to 1024 to reflect the new limit used by the PCI DriverLoaderLib included with Mac OS 8. Cosmetic documentation and code changes.